﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using DimOS.Constants;
using DimOS.DimFS;
using DimOS.DimSH;
using DimOS.Internals;
using DimOS.Plugs;
using Array = DimOS.Plugs.Array;
using Convert = DimOS.Plugs.Convert;
using Math = System.Math;
using String = DimOS.Plugs.String;
using SH = DimOS.DimSH.Shell;

namespace DimOS.H
{
    public static class Functions
    {
        public static List<FunctionEntry> Core = new List<FunctionEntry>();

        public static List<FunctionEntry> List = new List<FunctionEntry>();

        public static List<FunctionEntry> Operations = new List<FunctionEntry>();

        public static List<StackEntry> Stack = new List<StackEntry>();
    }

    class Absolute : Function
    { protected override Value Evaluate(Script source, bool silent = false)
    { return new Value(Math.Abs(source.Execute(Tokens.ParameterRight).Number)); } }

    class SquareRoot : Function
    { protected override Value Evaluate(Script source, bool silent = false) { var temp =
    source.Execute(Tokens.ParameterRight).Number; return new Value((float)(Math.Sqrt(temp))); } }
    
    class Round : Function
    { protected override Value Evaluate(Script source, bool silent = false) { var temp =
    source.Execute(Tokens.ParameterRight).Number; return new Value(temp.Round()); } }

    class Ceiling : Function
    { protected override Value Evaluate(Script source, bool silent = false) { var temp =
      source.Execute(Tokens.ParameterRight).Number; return new Value(temp.Ceiling()); } }

    class Floor : Function
    { protected override Value Evaluate(Script source, bool silent = false) { var temp =
    source.Execute(Tokens.ParameterRight).Number; return new Value(temp.Floor()); } }

    class Empty : Function
    { protected override Value Evaluate(Script source, bool silent = false)
    { return source.Execute(Tokens.ParameterRight); } }

    class Conditional : Function
    { protected override Value Evaluate(Script source, bool silent = false)
    { return Parser.ProcessConditional(source); } }

    class Break : Function
    { protected override Value Evaluate(Script source, bool silent = false)
    { return new Value(ValueType.Break); } }

    class Continue : Function
    { protected override Value Evaluate(Script source, bool silent = false)
    { return new Value(ValueType.Continue); } }

    class Return : Function
    { protected override Value Evaluate(Script source, bool silent = false) { if (source.Valid
    && source.Data[source.Pointer] == Tokens.Space) source.Traverse(1); var result = source
    .GetValue(); source.Pointer = source.Data.Length; result.Return = true; return result; } }

    class Try : Function
    { protected override Value Evaluate(Script source, bool silent = false)
    { return Parser.ProcessTry(source); } }

    class Throw : Function
    { protected override Value Evaluate(Script source, bool silent = false)
    { throw new InvalidOperationException(source.GetValue().ToString()); } }

    class SafetyPlug : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            if (source.Valid && source.Data[source.Pointer] == Tokens.Space) source.Traverse(1);
            if (source.Pointer > 0) { if (source.Data[source.Pointer - 1] == Tokens.ParameterLeft)
            source.Traverse(-1); else if (source.Data[source.Pointer - 1] == Tokens.CommandLeft)
            source.Traverse(-1); else if ((source.Data[source.Pointer - 1].ToString() + source
            .Data[source.Pointer]) == Tokens.TryShort) source.Traverse(-1); } if (source.Data
            [source.Pointer] == Tokens.ParameterLeft) Parser.SkipParameters(source); if (source
            .Data[source.Pointer] == Tokens.CommandLeft) { Parser.SkipChunk(source); Parser
            .SkipElse(source); return Value.Empty; } var temp = new Script(source); var next
            = temp.GetConstruct(); if (next == Tokens.If || (String.StartsWith(next, Tokens
            .IfShort) && !String.StartsWith(next, Tokens.ElseShort))) Parser.SkipIf(source);
            else if (next == Tokens.Loop || String.StartsWith(next, Tokens.LoopShort.ToString()))
            Parser.SkipLoops(source); else if (next == Tokens.Try || (temp.Pointer + 1 < temp.Data
            .Length && (temp.Data[temp.Pointer].ToString() + temp.Data[temp.Pointer + 1]) == Tokens
            .TryShort)) Parser.SkipTry(source); else { var skip = false; for (int i = 0; i < Commands
            .List[0].Count; i++) { var key = Commands.List[0][i].Key; if (String.StartsWith(next, key
            + Tokens.Dot) || next == key) { skip = true; break; } for (int j = 0; j < Commands.List
            [0][i].Aliases.Length; j++) { var alias = Commands.List[0][i].Aliases[j]; if (String
            .StartsWith(next, alias + Tokens.Dot)) { skip = true; break; } } } if (skip) Parser
            .SkipShell(source); else Parser.SkipChunk(source); } return Value.Empty;
        }
    }

    class Literal : Function
    { protected override Value Evaluate(Script source, bool silent = false) { if (Item
    .Length > 1 && Item[0] == Tokens.VerbatimString && Item[Item.Length - 1] ==  Tokens
    .VerbatimString) return new Value(String.Unmirror(Item.Substring(1, Item.Length - 2),
    Tokens.VerbatimString)); var number = 0f; try { number = Convert.ToFloat(Item); }
    catch { } return new Value (number); } public string Item { private get; set; } }

    class Loop : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var start = source.Pointer; var condition = source.GetChunk(Tokens.ParameterLeft,
            Tokens.ParameterRight); source.Traverse(1); var index = -1; var counter = -1;
            var trigger = false; int commandLeft = 0, commandRight = 0, parameterLeft = 0,
            parameterRight = 0; var parameters = new List<string>(); var statements = new
            List<string>(); for (int i = 0; i < condition.Length; i++) { if (condition[i]
            == Tokens.VerbatimString && (!trigger || (condition[i - 1] != Tokens.MirrorLeft
            && condition[i - 1] != Tokens.MirrorRight))) trigger = !trigger; else if (!trigger
            && condition[i] == Tokens.CommandLeft) commandLeft++; else if (!trigger && condition
            [i] == Tokens.CommandRight) commandRight++; else if (!trigger && condition[i] ==
            Tokens.ParameterLeft) parameterLeft++; else if (!trigger && condition[i] == Tokens
            .ParameterRight) parameterRight++; else if (start != -1 && !trigger && (condition[i]
            == Tokens.Index && (i + 1 < condition.Length && condition[i + 1] == Tokens.Index) &&
            ((commandLeft == 0 && parameterLeft == 0) || (commandLeft == commandRight && parameterLeft
            == parameterRight)))) start = -1; else if (!trigger && condition[i] == Tokens.Separator &&
            ((commandLeft == 0 && parameterLeft == 0) || (commandLeft == commandRight && parameterLeft
            == parameterRight))) { parameters.Add(condition.Substring(index + 1, i - index - 1) + Tokens
            .End); index = i; } else if (!trigger && condition[i] == Tokens.End && ((commandLeft == 0
            && parameterLeft == 0) || (commandLeft == commandRight && parameterLeft == parameterRight)))
            { statements.Add(condition.Substring(counter + 1, i - counter - 1) + Tokens.End); counter
            = i; } } if (index != condition.Length - 1) parameters.Add(condition.Substring(index + 1,
            condition.Length - index - 1) + Tokens.End); if (counter != condition.Length - 1) statements
            .Add(condition.Substring(counter + 1, condition.Length - counter - 1) + Tokens.End); Value
            result; if (parameters.Count == 0) { Parser.SkipLoops(source, true); if (!Array.Contains
            (Tokens.ListStop, source.Data[source.Pointer])) source.Traverse(-1); } if (parameters.Count
            > 1) result = Parser.ProcessFor(source, parameters); else if (statements.Count > 1) result
            = Parser.ProcessFor(source, statements); else if (start == -1) result = Parser.ProcessForeach
            (source, condition); else { source.Pointer = start; result = Parser.ProcessWhile(source); }
            return result.Type == ValueType.Break || result.Type == ValueType.Continue ? Value.Empty : result;
        }
    }

    class Variable : Function
    {
        public Value Value; public int Level; public List<Value> Specifiers;

        internal Variable(Value value) { Value = value; Type = ValueType.Argument; }

        protected override Value Evaluate(Script source, bool silent = false)
        {
            if ((source.Pointer >= 1 ? source.Data[source.Pointer - 1] : Tokens.Empty)
            == Tokens.ValueLeft) { if ((Value.Type == ValueType.List && (Value.List ==
            null || Value.List.Count == 0)) || (Value.Type == ValueType.String && string
            .IsNullOrEmpty(Value.String)) || Value.Type == ValueType.Number) throw new
            InvalidOperationException("Value is not a list or contains no elements.");               
            if (Specifiers == null) { var name = source.Substring(source.Pointer - 1);
            Specifiers = Value.ElementSpecifiers(ref name, ref Level); } source.Traverse
            (Level); if (Value.Type == ValueType.String && (Specifiers.Count == 0 ||
            Specifiers.Count > 1)) throw new InvalidOperationException("String elements"
            + " should be accessed with single specifier."); if (Value.Type == ValueType
            .String && (Specifiers[0].Type != ValueType.Number || Specifiers[0] == Value
            .Empty)) throw new InvalidOperationException("Non-numeric element specifier"
            + " (" + Specifiers[0] + ") encountered."); if (Value.Type == ValueType.List)
            return Value[Specifiers]; var character = Convert.ToInt32(Specifiers[0].Number
            .Round()); if (character < 0) character = Value.String.Length - Math.Abs
            (character); if (character < 0 || character >= Value.String.Length) throw
            new InvalidOperationException("Index (" + character + ") was out of " +
            "range [" + Value.String.Length + "]."); return new Value(Value.String
            [character].ToString()); } return Value;
        }
    }

    class Type : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var variable = source.GetConstruct(new[] {Tokens.ParameterRight});
            if (!source.Valid) throw new InvalidOperationException("Unmatched"
            + " parentheses for the " + Name + "()."); var specifiers = Value
            .ElementSpecifiers(ref variable); var function = Find(variable);
            if (function == null) throw new InvalidOperationException("Variable"
            + " (" + variable + ") is not registered."); var value = function
            .Call(source); if (specifiers.Count > 0) { value = value[specifiers];
            if (source.Valid && source.Data[source.Pointer] == Tokens.ValueRight)
            source.Pointer++; } if (source.Valid && (source.Data[source.Pointer]
            == Tokens.ParameterRight || source.Data[source.Pointer] == Tokens
            .Space)) source.Traverse(1); switch (value.Type) { case ValueType
            .Number: return new Value("Number"); case ValueType.String: return
            new Value("String"); case ValueType.List: return new Value("List");
            case ValueType.Break: return new Value("Break"); case ValueType
            .Continue: return new Value("Continue"); case ValueType.Function:
            return new Value("Function"); default: return new Value("None"); }
        }
    }

    class New : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {   
            if (source.Data[source.Pointer] == Tokens.ParameterLeft) { var path = source
            .GetValue(); if (!Path.Valid(path.String)) throw new InvalidOperationException
            ("Valid path expected instead of (" + path.String + ")."); Error error; var
            insufficientPrivileges = new List<string>(); var nonTraversable = new List
            <string>(); var list = Layout.List(path.String, SH.AccessMode, out error,
            ref nonTraversable, ref insufficientPrivileges, ref Layout.ListStub, ref
            Layout.NodeStub, false); Layout.ListStub.Clear(); Layout.NodeStub.Clear();
            if (list.Count == 0 && error != Error.None && error != Error.InvalidName)
            throw new InvalidOperationException("The path " + Path.Normalize(path.String)
            + (error == Error.NotFound ? " does not exist." : (nonTraversable.Count != 0
            && insufficientPrivileges.Count == 0 ? " is not traversable." : (nonTraversable
            .Count == 0 ? " cannot be accessed." : " is not traversable/accessible."))));
            var values = new List<Value>(); for (int i = 0; i < list.Count; i++) try {
            values.Add(new Script(Script.Normalize(list[i])).ExecuteAll()); } catch { } 
            return new Value(values); } var name = source.GetConstruct(Tokens.Separators);
            if (source.Valid && (source.Data[source.Pointer] == Tokens.ParameterLeft ||
            source.Data[source.Pointer] == Tokens.Space)) source.Traverse(1); var stop
            = String.IndexOfAny(source.Data, new[] { Tokens.ParameterRight }, source
            .Pointer); if (stop < 0) throw new InvalidOperationException("Incomplete "
            + "function definition encountered."); var arguments = String.Split(source
            .Substring(source.Pointer, stop - source.Pointer), Tokens.Separator.ToString
            (), true); source.Pointer = stop + 1; if (arguments.Count == 1 && string
            .IsNullOrWhiteSpace(arguments[0])) arguments.Clear(); if (source.Valid &&
            (source.Data[source.Pointer] == Tokens.CommandLeft || source.Data[source
            .Pointer] == Tokens.Space)) source.Traverse(1); Add(name, new Definition
            (name, source.GetChunk(Tokens.CommandLeft, Tokens.CommandRight), arguments));
            return new Value(name);
        }
    }

    class Definition : Function
    {
        public string Body; public List<string> Arguments;

        internal Definition(string name, string body, List<string> arguments)
        { Name = name; Body = body; Arguments = arguments; Type = ValueType.Function; }

        protected override Value Evaluate(Script source, bool silent = false)
        {
            bool list; var parameters = source.GetParameters(Tokens.ParameterLeft, Tokens
            .ParameterRight, out list); if (source.Valid && source.Pointer > 0 && source
            .Data[source.Pointer] == Tokens.CommandLeft) source.Traverse(-1); if (parameters
            .Count != Arguments.Count) throw new InvalidOperationException("(" + parameters
            .Count + ") parameters supplied for " + Name + "([" + Arguments.Count + "]).");
            var entry = new StackEntry(Name); for (int i = 0; i < Arguments.Count; i++) entry
            .Functions.Add(new FunctionEntry(Arguments[i], new Variable(parameters[i])));
            Functions.Stack.Add(entry); var result = new Script(Body).ExecuteAll(); Functions
            .Stack.RemoveAt(Functions.Stack.Count - 1); result.Return = false; return result;
        }
    }

    class InputOutput : Function
    { protected override Value Evaluate(Script source, bool silent = false) { var list = false;
      List<Value> parameters; if (Array.Contains(Tokens.ListProcess, source.Data[source.Pointer]))
      return Engine.Input(); if (source.Valid && source.Pointer > 0 && source.Data[source.Pointer -
      1] == Tokens.Space && source.Data[source.Pointer] == Tokens.ParameterLeft) source.Traverse(1);
      if (source.Valid && source.Pointer > 0 && source.Data[source.Pointer - 1] != Tokens.ParameterLeft)
      parameters = new List<Value> { source.GetValue() }; else parameters = source.GetParameters(Tokens
      .ParameterLeft, Tokens.ParameterRight, out list); if (parameters.Count == 0 || parameters[0].Type
      == ValueType.None || (list && parameters[0].List.Count == 0)) return Engine.Input(); var data = "";
      for (int i = 0; i < parameters.Count; i++) data += parameters[i].ToString(); Engine.Output(data);
      return new Value(data); } }

    class IncDec : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var prefix = string.IsNullOrWhiteSpace(Name); if (prefix) Name = source
            .GetConstruct(Tokens.Separators); var specifiers = Value.ElementSpecifiers
            (ref Name); var value = new Value(0f); var function = Find(Name); if (function
            == null) AddVariable(Name, new Variable(value)); else value = function.Call
            (source); if (specifiers.Count > 0 || (source.Pointer < source.Data.Length ?
            source.Data[source.Pointer] : Tokens.Empty) == Tokens.ValueLeft) { if (prefix)
            { var name = Name + source.Substring(source.Pointer, Engine.ProcessLimit); var
            end = 0; specifiers = Value.ElementSpecifiers(ref name, ref end); if (end -
            name.Length < 0) end = 0; source.Traverse(end); } value = value[specifiers];
            if (source.Valid && source.Data[source.Pointer] == Tokens.ValueRight) source
            .Traverse(1); } var temp = Value.Empty; if (value.Type == ValueType.Number)
            { temp = new Value(value.Number); value.Number += Operation == Tokens.Increment
            ? 1 : -1; } else if (value.Type == ValueType.String) { temp = new Value(value
            .String); value.String = Operation == Tokens.Increment ? value.String.Increment
            () : value.String.Decrement(); } else if (value.Type == ValueType.List) { temp
            = new Value(value.List); if (Operation == Tokens.Increment) value.List.Add(new
            Value(0f)); else value.List.RemoveAt(value.List.Count - 1); } AddVariable(Name,
            new Variable(value)); return prefix ? value : temp;
        }

        override protected Function New() { return new IncDec(); }
    }

    class SortReverse : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var prefix = string.IsNullOrWhiteSpace(Name); if (prefix) Name = source
            .GetConstruct(Tokens.Separators); var specifiers = Value.ElementSpecifiers
            (ref Name); var value = new Value(0f); var function = Find(Name); if (function
            == null) AddVariable(Name, new Variable(value)); else value = function.Call
            (source); if (specifiers.Count > 0 || (source.Pointer < source.Data.Length ?
            source.Data[source.Pointer] : Tokens.Empty) == Tokens.ValueLeft) { if (prefix)
            { var name = Name + source.Substring(source.Pointer, Engine.ProcessLimit); var
            end = 0; specifiers = Value.ElementSpecifiers(ref name, ref end); if (end -
            name.Length < 0) end = 0; source.Traverse(end); } value = value[specifiers];
            if (source.Valid && source.Data[source.Pointer] == Tokens.ValueRight) source
            .Traverse(1); } var temp = Value.Empty; if (value.Type == ValueType.Number)
            { temp = new Value(value.Number); value.Number = Operation == Tokens.Reverse
            ? value.Number * -1 : value.Number; } else if (value.Type == ValueType.String)
            { temp = new Value(value.String); value.String = Operation == Tokens.Reverse ?
            value.String.Reverse() : value.String.Sort(); } else if (value.Type == ValueType
            .List) { temp = new Value(value.List); if (Operation == Tokens.Sort) List
            .SortNaturally(value.List, 0, value.List.Count - 1); else List.Reverse(value
            .List); } AddVariable(Name, new Variable(value)); return prefix ? value : temp;
        }

        override protected Function New() { return new SortReverse(); }
    }

    class CombinedSize : Function
    { protected override Value Evaluate(Script source, bool silent = false) { var list = false;
      List<Value> parameters; if (Array.Contains(Tokens.ListProcess, source.Data[source.Pointer]))
      return new Value(Tokens.NewLine.ToString()); if (source.Valid && source.Pointer > 0 && source
      .Data[source.Pointer - 1] == Tokens.Space && source.Data[source.Pointer] == Tokens.ParameterLeft)
      source.Traverse(1); if (source.Valid && source.Pointer > 0 && source.Data[source.Pointer - 1] !=
      Tokens.ParameterLeft) parameters = new List<Value> { source.GetValue() }; else parameters = source
      .GetParameters(Tokens.ParameterLeft, Tokens.ParameterRight, out list); if (parameters.Count == 0
      || parameters[0].Type == ValueType.None || (list && parameters[0].List.Count == 0)) return new
      Value(Tokens.NewLine.ToString()); var sizeCount = 0; for (int i = 0; i < parameters.Count; i++)
      sizeCount += parameters[i].Type == ValueType.List ? parameters[i].List.Count : parameters[i]
      .ToString().Length; return new Value(sizeCount); } }

    class Size : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var variable = source.GetConstruct(new[] {Tokens.ParameterRight});
            if (!source.Valid) throw new InvalidOperationException("Unmatched"
            + " parentheses for the " + Name + "()."); var specifiers = Value
            .ElementSpecifiers(ref variable); var function = Find(variable);
            if (function == null) throw new InvalidOperationException("Variable"
            + " (" + variable + ") is not registered."); var value = function
            .Call (source); if (specifiers.Count > 0) { value = value[specifiers];
            if (source.Valid && source.Data[source.Pointer] == Tokens.ValueRight)
            source.Pointer++; }
            /*var value = source.GetValue();*/ if (source.Valid && (source.Data[source.Pointer]
            == Tokens.ParameterRight || source.Data[source.Pointer] == Tokens.Space)) source
            .Traverse(1); if (value.Type == ValueType.List) return new Value(value.List.Count);
            return new Value(value.ToString().Length);
        }
    }

    class Wait : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var value = source.GetValue(); if (value.Type == ValueType
            .Number) Engine.Wait(Convert.ToInt32(Math.Abs(value.Number
            .Round()))); else if (value.Type == ValueType.List) Engine
            .Wait(value.List.Count); else if (value.Type == ValueType
            .String) Engine.Wait(value.String.Length); return new Value
            (value);
        }
    }

    class Assignment : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var value = source.GetValue(); var specifiers = Value
            .ElementSpecifiers(ref Name); if (specifiers.Count == 0)
            { AddVariable(Name, new Variable(value)); return value; }
            var function = Find(Name); var parent = function != null
            ? function.Call(source) : new Value(); if (parent.Type ==
            ValueType.List || parent.Type == ValueType.None) { Parser
            .ExtendList(parent, specifiers, 0, value); AddVariable
            (Name, new Variable(parent)); return parent; } if (parent
            .Type ==ValueType.String && specifiers.Count > 1) throw
            new InvalidOperationException("String elements should be"
            + " accessed with single specifier."); if (parent.Type ==
            ValueType.String && specifiers[0].Type != ValueType.Number)
            throw new InvalidOperationException("Non-numeric element"
            + " specifier (" + specifiers[0] + ") encountered."); var
            character = Convert.ToInt32(specifiers[0].Number.Round());
            if (character < 0) character = parent.String.Length - Math
            .Abs(character); if (character < 0 || character >= parent
            .String.Length) throw new InvalidOperationException("Index ("
            + character + ") was out of range [" + parent.String.Length +
            "]."); var temp = parent.String.ToCharArray(); temp[character]
            = value.ToString()[0]; parent.String = new string(temp); return
            new Value(parent); 
        }

        override protected Function New() { return new Assignment(); }
    }

    class CombinedAssignment : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var value = source.GetValue(); var specifiers = Value.ElementSpecifiers
            (ref Name); var function = Find(Name); var temp = function != null ?
            function.Call(source) : new Value(); var parent = temp; if (specifiers
            .Count > 0) { if (function != null && temp.Type == ValueType.String)
            throw new InvalidOperationException("Operation (" + Operation + ") "
            + "cannot be applied to string elements."); Parser.ExtendList(parent,
            specifiers, 0, new Value()); temp = temp[specifiers]; if (source.Valid
            && source.Data[source.Pointer] == Tokens.ValueRight) source.Traverse(1);
            } temp.Action = Operation.Remove(Operation.Length - 1); if ((temp.Type
            == ValueType.None || temp.Type == ValueType.Number) && value.Type ==
            ValueType.Number) Parser.ProcessNumbers(temp,value); else if (((temp.Type
            == ValueType.None && specifiers.Count > 0) || temp.Type == ValueType.List)
            || value.Type == ValueType.List) Parser.ProcessLists(temp, value); else
            Parser.ProcessStrings(temp, value); AddVariable(Name, specifiers.Count
            > 0 ? new Variable(parent) : new Variable(temp)); return temp;
        }

        override protected Function New() { return new CombinedAssignment(); }
    }

    class Substring : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            bool list; var parameters = source.GetParameters(Tokens.ParameterLeft,
            Tokens.ParameterRight, out list); if (source.Valid && source.Pointer >
            0 && source.Data[source.Pointer] == Tokens.CommandLeft) source.Traverse
            (-1); if (parameters == null || parameters.Count < 2 || parameters.Count
            > 3) throw new InvalidOperationException("(" + (parameters == null ? 0 :
            parameters.Count) + ") parameters supplied for " + Name + "([2|3]).");
            var value = parameters[0].String; if (parameters[0].Type != ValueType
            .String) throw new InvalidOperationException(Name + "() function can "
            + "be applied only to strings."); var start = Convert.ToInt32(parameters
            [1].Number.Round()); if (start < 0) start = value.Length - Math.Abs
            (start); var end = parameters.Count == 2 ? value.Length - 1 : Convert
            .ToInt32(parameters[2].Number.Round()); if (end < 0) end = value.Length
            - Math.Abs(end); if (parameters[1].Type != ValueType.Number) throw new
            InvalidOperationException("Starting index (" + start +") for " + Name
            + "() must be a numeric value."); if (start > value.Length - 1) throw new
            InvalidOperationException("Starting index (" + start + ") was bigger than "
            + "string length [" + value.Length + "]."); if (parameters.Count == 3) { if
            (parameters[2].Type != ValueType.Number) throw new InvalidOperationException
            ("Ending index (" + end +") for " + Name + "() must be a numeric value.");
            if (end > value.Length - 1) throw new InvalidOperationException("Ending " +
            "index (" + end + ") was bigger than string length [" + value.Length + "].");
            } if (start <= end) return new Value(value.Substring(start, end - start + 1));
            return new Value(value.Substring(end, start - end + 1).Reverse());
        }
    }

    class Replace : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            bool list; var parameters = source.GetParameters(Tokens.ParameterLeft, Tokens
            .ParameterRight, out list); if (source.Valid && source.Pointer > 0 && source
            .Data[source.Pointer] == Tokens.CommandLeft) source.Traverse(-1); if (parameters
            == null || parameters.Count != 3) throw new InvalidOperationException("(" +
            (parameters == null ? 0 : parameters.Count) + ") parameters supplied for " +
            Name + "([3])."); var value = parameters[0].String; if (parameters[0].Type !=
            ValueType.String) throw new InvalidOperationException(Name + "() function " +
            "can be applied only to strings."); return new Value(String.Replace(value,
            parameters[1].ToString(), parameters[2].ToString()));
        }
    }

    class InputRedirect : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            var value = source.GetValue(); if (Path.Valid(value.String)) {
            Error error; var insufficientPrivileges = new List<string>();
            var nonTraversable = new List<string>(); var list = Layout.List
            (value.String, SH.AccessMode, out error, ref nonTraversable, ref
            insufficientPrivileges, ref Layout.ListStub, ref Layout.NodeStub,
            false); Layout.ListStub.Clear(); Layout.NodeStub.Clear(); if (list
            .Count == 0 && error != Error.None && error != Error.InvalidName)
            throw new InvalidOperationException("The path " + Path.Normalize
            (value.String) + (error == Error.NotFound ? " does not exist." :
            (nonTraversable.Count != 0 && insufficientPrivileges.Count == 0 ?
            " is not traversable." : (nonTraversable.Count == 0 ? " cannot be"
            + "accessed." : " is not traversable/accessible.")))); value.List =
            new List<Value>(); for (int i = 0; i < list.Count; i++) value.List
            .Add(new Value(list[i])); } var specifiers = Value.ElementSpecifiers
            (ref Name); if (specifiers.Count == 0) { AddVariable(Name, new Variable
            (value)); return value; } var function = Find(Name); var parent =
            function != null ? function.Call(source) : new Value(); if (parent
            .Type == ValueType.List || parent.Type == ValueType.None) { Parser
            .ExtendList(parent, specifiers, 0, value); AddVariable(Name, new
            Variable(parent)); return parent; } if (parent.Type ==ValueType.String
            && specifiers.Count > 1) throw new InvalidOperationException("String "
            + "elements should be accessed wit single specifier."); if (parent.Type
            == ValueType.String && specifiers [0].Type != ValueType.Number) throw
            new InvalidOperationException("Non-numeric element specifier (" +
            specifiers[0] + ") encountered."); var character = Convert.ToInt32
            (specifiers[0].Number.Round()); if (character < 0) character = parent
            .String.Length - Math.Abs(character); if (character < 0 || character >=
            parent.String.Length) throw new InvalidOperationException("Index (" +
            character + ") was out of range [" + parent.String.Length + "]."); var
            temp = parent.String.ToCharArray(); temp[character] = value.ToString()
            [0]; parent.String = new string(temp); return new Value(parent); 
        }

        override protected Function New() { return new InputRedirect(); }
    }

    class Shell : Function
    {
        protected override Value Evaluate(Script source, bool silent = false)
        {
            Error error; int level = 0, commandLeft = 0, commandRight = 0; string result; 
            var command = Name; if (source.Data[source.Pointer - 1] == Tokens.CommandLeft)
            source.Traverse(-1); else if (source.Data[source.Pointer - 1] != Tokens.Dot &&
            source.Data[source.Pointer] == Tokens.Dot) source.Traverse(1); if (source.Data
            [source.Pointer - 1] == Tokens.Dot) { level++; command += Tokens.Dot; for (int
            i = source.Pointer; i < source.Data.Length; i++) { if (source.Data[i] == Tokens
            .Dot && command[command.Length - 1] != Tokens.Dot) level++; else if (source.Data
            [i] == Tokens.CommandLeft || source.Data[i] == Tokens.Space) { source.Pointer =
            i + 1; break; } else if (source.Data[i] == Tokens.CommandRight || source.Data[i]
            == Tokens.Separator || source.Data[i] == Tokens.End) { source.Pointer = i; break;
            } if (source.Data[i] != Tokens.Dot || command[command.Length - 1] != Tokens.Dot)
            command += source.Data[i]; } if (command[command.Length - 1] == Tokens.Dot) {
            command = command.Remove(command.Length - 1, 1); level--; } } var entry = Command
            .Find(level, command.Remove(0, command.LastIndexOf(Tokens.Dot) == - 1 ? 0 : command
            .LastIndexOf(Tokens.Dot) + 1), out error); if (entry == null) { if (error == Error
            .WrongLevel || error == Error.NotFound) throw new InvalidOperationException("Command"
            + " (" + command + ") not found."); throw new InvalidOperationException("Failed"
            + " to invoke command (" + command + ")."); } if (source.Data[source.Pointer] ==
            Tokens.Separator || source.Data[source.Pointer] == Tokens.End || source.Data[source
            .Pointer] == Tokens.CommandRight) { if (!silent) Engine.Header(command); entry.Action
            .Invoke(new List<string>(), command.LastIndexOf(Tokens.Dot) == -1 ? "" : command
            .Remove(command.LastIndexOf(Tokens.Dot)), silent, out result, out error); } else {
            var parameters = new List<string>{""}; var trigger = false; while (source.Pointer
            < source.Data.Length) { if (source.Data[source.Pointer] == Tokens.VerbatimString &&
            (source.Pointer - 1 < 0 || (source.Data [source.Pointer - 1] != Tokens.MirrorLeft &&
            source.Data[source.Pointer - 1] != Tokens.MirrorRight))) trigger = !trigger; else if
            (!trigger && source.Data[source.Pointer] == Tokens.CommandLeft) { if (commandLeft == 0)
            level = source.Pointer + 1; commandLeft++; } else if (!trigger && source.Data[source
            .Pointer] == Tokens.CommandRight) { commandRight++; if (commandLeft == commandRight)
            { commandLeft = commandRight = 0; parameters[parameters.Count - 1] += new Script(Script
            .Normalize(source.Data.Substring(level, source.Pointer - level))).ExecuteAll().ToString
            (); } else if (commandLeft < commandRight) break; } else if (!trigger && (source.Data
            [source.Pointer] == Tokens.End || source.Data[source.Pointer] == Tokens.Separator) &&
            commandLeft == 0 && commandRight == 0) break; else if (!trigger && source.Data[source
            .Pointer] == Tokens.Space && commandLeft == 0 && commandRight == 0) { parameters[parameters
            .Count - 1] = parameters[parameters.Count - 1].Unmirror(Tokens.VerbatimString); parameters
            .Add(""); } else if (commandLeft == 0 && commandRight == 0) parameters[parameters.Count
            - 1] += source.Data[source.Pointer]; source.Traverse(1); } if (source.Valid) if (source
            .Data[source.Pointer - 1] != Tokens.Space) parameters[parameters.Count - 1] = parameters
            [parameters.Count - 1].Unmirror(Tokens.VerbatimString); else parameters.RemoveAt(parameters
            .Count - 1); if (!silent) Engine.Header(command, parameters); entry.Action.Invoke(parameters,
            command.LastIndexOf(Tokens.Dot) == -1 ? "" : command.Remove(command.LastIndexOf(Tokens
            .Dot)), silent, out result, out error); } if (error == Error.None) return new Value
            (result); if (error == Error.InsufficientPrivileges) throw new InvalidOperationException
            ("Insufficient privileges" + (result == "" ? "." : " for " + result + ".")); if (error ==
            Error.InvalidArgument) throw new InvalidOperationException("Invalid argument " + (result
            == "" ? "" : "(" + result + ") ") + "found at [" + command + "]."); if (error == Error
            .InvalidName) throw new InvalidOperationException("Invalid name " + (result == "" ? ""
            : "(" + result + ") ") + "passed to [" + command + "]."); if (error == Error.NotFound)
            throw new InvalidOperationException("Node " + (result == "" ? "" : result + " ") + "was"
            + " not found."); if (error == Error.NotTraversable) throw new InvalidOperationException
            ("Node " + (result == "" ? "" : result + " ") + "is not traversable."); if (error ==
            Error.Mismatch) throw new InvalidOperationException("Mismatched parameters " + (result
            == "" ? "" : "(" + result + ") ") + "passed to [" + command + "]."); if (error == Error
            .Other) throw new InvalidOperationException(result); if (error == Error.Unknown) throw
            new InvalidOperationException("Unknown error at [" + command + "]."); if (error == Error
            .WrongParent) throw new InvalidOperationException("Child command (" + (Name.Length + 1 <
            command.Length ? command.Remove(0, Name.Length + 1) : command) + ") is not valid for " +
            "parent [" + Name + "]."); return new Value(result);
        }
    }
}
